Micron Document
🎖️GitЯра🎖️

Node / meshtastic / Meshtastic-Android / files / core / takserver / src / commonTest / kotlin / org / meshtastic / core / takserver / MeshToCotBroadcasterTest.kt

Displaying Raw • Download

core/takserver/src/commonTest/kotlin/org/meshtastic/core/takserver/MeshToCotBroadcasterTest.kt 65a1f5ce4d0e4b8eafcd0bbfccafd41dc6ee888a (65a1f5ce) Text, 9.01 KB

T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.core.takserver

Tff7b72import T7ee787kotlinx.coroutines.test.TestScope
Tff7b72import T7ee787kotlinx.coroutines.test.UnconfinedTestDispatcher
Tff7b72import T7ee787kotlinx.coroutines.test.advanceTimeBy
Tff7b72import T7ee787kotlinx.coroutines.test.runCurrent
Tff7b72import T7ee787kotlinx.coroutines.test.runTest
Tff7b72import T7ee787org.meshtastic.core.di.CoroutineDispatchers
Tff7b72import T7ee787org.meshtastic.core.model.MyNodeInfo
Tff7b72import T7ee787org.meshtastic.core.model.Node
Tff7b72import T7ee787org.meshtastic.core.testing.FakeNodeRepository
Tff7b72import T7ee787org.meshtastic.core.testing.FakeTakPrefs
Tff7b72import T7ee787kotlin.test.Test
Tff7b72import T7ee787kotlin.test.assertEquals
Tff7b72import T7ee787kotlin.test.assertTrue
Tff7b72import T7ee787kotlin.time.Clock

Tff7b72class T56d364MeshToCotBroadcasterTest Tb4b4b4{
T8b949e// FakeTAKServerManager lives in its own file in this source set, shared with TAKMeshIntegrationTest.
T8b949e// It never touches isRunning; MeshToCotBroadcaster only reads connectionCount.

Tff7b72private Tff7b72class T56d364HarnessTb4b4b4(Tff7b72val Te6edf3scopeTb4b4b4: Te6edf3TestScopeTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3serverManager Tff7b72= Te6edf3FakeTAKServerManagerTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3nodeRepository Tff7b72= Te6edf3FakeNodeRepositoryTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3takPrefs Tff7b72= Te6edf3FakeTakPrefsTb4b4b4(Tb4b4b4)
Tff7b72private Tff7b72val Te6edf3dispatcher Tff7b72= Te6edf3UnconfinedTestDispatcherTb4b4b4(Te6edf3scopeTb4b4b4.Te6edf3testSchedulerTb4b4b4)

Tff7b72val Te6edf3broadcaster Tff7b72=
Te6edf3MeshToCotBroadcasterTb4b4b4(
Te6edf3takServerManager Tff7b72= Te6edf3serverManagerTb4b4b4,
Te6edf3nodeRepository Tff7b72= Te6edf3nodeRepositoryTb4b4b4,
Te6edf3takPrefs Tff7b72= Te6edf3takPrefsTb4b4b4,
Te6edf3dispatchers Tff7b72= Te6edf3CoroutineDispatchersTb4b4b4(Te6edf3io Tff7b72= Te6edf3dispatcherTb4b4b4, Te6edf3main Tff7b72= Te6edf3dispatcherTb4b4b4, Te6edf3default Tff7b72= Te6edf3dispatcherTb4b4b4)Tb4b4b4,
Tb4b4b4)

T8b949e/** Simulates an attached TAK client; without one the broadcaster deliberately stays quiet. */
Tff7b72fun Td2a8ffwithConnectedClientTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3apply Tb4b4b4{ Te6edf3serverManagerTb4b4b4.Te6edf3connectionsTb4b4b4.Te6edf3value Tff7b72= T79c0ff1 Tb4b4b4}

Tff7b72fun Td2a8ffenableMeshToCotTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3apply Tb4b4b4{ Te6edf3takPrefsTb4b4b4.Te6edf3isMeshToCotEnabledTb4b4b4.Te6edf3value Tff7b72= Tff7b72true Tb4b4b4}

Tff7b72fun Td2a8ffsetOurNodeNumTb4b4b4(Te6edf3numTb4b4b4: Tffa657IntTb4b4b4) Tff7b72= Te6edf3apply Tb4b4b4{ Te6edf3nodeRepositoryTb4b4b4.Te6edf3setMyNodeInfoTb4b4b4(Te6edf3myNodeInfoTb4b4b4(Te6edf3numTb4b4b4)Tb4b4b4) Tb4b4b4}

T8b949e/** Lets the broadcaster's inter-message spacing elapse so every queued send lands. */
Tff7b72fun Td2a8ffsettleTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3scopeTb4b4b4.Te6edf3advanceTimeByTb4b4b4(Te6edf3SETTLE_MSTb4b4b4)
Te6edf3scopeTb4b4b4.Te6edf3runCurrentTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ff`stays silent while the preference is off`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3h Tff7b72= Te6edf3HarnessTb4b4b4(Tff7b72thisTb4b4b4)Tb4b4b4.Te6edf3withConnectedClientTb4b4b4(Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3nodeRepositoryTb4b4b4.Te6edf3setNodesTb4b4b4(Te6edf3listOfTb4b4b4(Te6edf3meshNodeTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4)

Te6edf3hTb4b4b4.Te6edf3broadcasterTb4b4b4.Te6edf3startTb4b4b4(Te6edf3backgroundScopeTb4b4b4)
Te6edf3hTb4b4b4.Te6edf3settleTb4b4b4(Tb4b4b4)

Te6edf3assertTrueTb4b4b4(Te6edf3hTb4b4b4.Te6edf3serverManagerTb4b4b4.Te6edf3broadcastsTb4b4b4.Te6edf3isEmptyTb4b4b4(Tb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffopt-in pref off must publish nothingTa5d6ff"Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ff`publishes eligible nodes once enabled`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3h Tff7b72= Te6edf3HarnessTb4b4b4(Tff7b72thisTb4b4b4)Tb4b4b4.Te6edf3withConnectedClientTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3enableMeshToCotTb4b4b4(Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3nodeRepositoryTb4b4b4.Te6edf3setNodesTb4b4b4(Te6edf3listOfTb4b4b4(Te6edf3meshNodeTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4)

Te6edf3hTb4b4b4.Te6edf3broadcasterTb4b4b4.Te6edf3startTb4b4b4(Te6edf3backgroundScopeTb4b4b4)
Te6edf3hTb4b4b4.Te6edf3settleTb4b4b4(Tb4b4b4)

Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3hTb4b4b4.Te6edf3serverManagerTb4b4b4.Te6edf3broadcastsTb4b4b4.Te6edf3sizeTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6ffMESHTASTIC-A1B2C3D4Ta5d6ff"Tb4b4b4, Te6edf3hTb4b4b4.Te6edf3serverManagerTb4b4b4.Te6edf3broadcastsTb4b4b4.Te6edf3singleTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3uidTb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ff`does not publish without a connected TAK client`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
T8b949e// Broadcasting with no client would fill TAKServerManager's 50-entry offline queue with
T8b949e// node PLIs and evict genuine mesh CoT. The replay on connect covers the gap.
Tff7b72val Te6edf3h Tff7b72= Te6edf3HarnessTb4b4b4(Tff7b72thisTb4b4b4)Tb4b4b4.Te6edf3enableMeshToCotTb4b4b4(Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3nodeRepositoryTb4b4b4.Te6edf3setNodesTb4b4b4(Te6edf3listOfTb4b4b4(Te6edf3meshNodeTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4)

Te6edf3hTb4b4b4.Te6edf3broadcasterTb4b4b4.Te6edf3startTb4b4b4(Te6edf3backgroundScopeTb4b4b4)
Te6edf3hTb4b4b4.Te6edf3settleTb4b4b4(Tb4b4b4)

Te6edf3assertTrueTb4b4b4(Te6edf3hTb4b4b4.Te6edf3serverManagerTb4b4b4.Te6edf3broadcastsTb4b4b4.Te6edf3isEmptyTb4b4b4(Tb4b4b4)Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ff`skips ineligible nodes`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3h Tff7b72= Te6edf3HarnessTb4b4b4(Tff7b72thisTb4b4b4)Tb4b4b4.Te6edf3withConnectedClientTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3enableMeshToCotTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3setOurNodeNumTb4b4b4(Te6edf3OUR_NODE_NUMTb4b4b4)
Te6edf3hTb4b4b4.Te6edf3nodeRepositoryTb4b4b4.Te6edf3setNodesTb4b4b4(
Te6edf3listOfTb4b4b4(
Te6edf3meshNodeTb4b4b4(Te6edf3num Tff7b72= Te6edf3OUR_NODE_NUMTb4b4b4)Tb4b4b4,
Te6edf3meshNodeTb4b4b4(Te6edf3num Tff7b72= T79c0ff0Te6edf3x22222222Tb4b4b4, Te6edf3latitudeI Tff7b72= T79c0ff0Tb4b4b4, Te6edf3longitudeI Tff7b72= T79c0ff0Tb4b4b4)Tb4b4b4,
Te6edf3meshNodeTb4b4b4(Te6edf3num Tff7b72= T79c0ff0Te6edf3x33333333Tb4b4b4, Te6edf3hwModel Tff7b72= Te6edf3orgTb4b4b4.Te6edf3meshtasticTb4b4b4.Te6edf3protoTb4b4b4.Te6edf3HardwareModelTb4b4b4.Te6edf3UNSETTb4b4b4)Tb4b4b4,
Te6edf3meshNodeTb4b4b4(Te6edf3num Tff7b72= T79c0ff0Te6edf3x44444444Tb4b4b4, Te6edf3lastHeard Tff7b72= T79c0ff1Tb4b4b4)Tb4b4b4,
Te6edf3meshNodeTb4b4b4(Te6edf3num Tff7b72= Te6edf3ELIGIBLE_NODE_NUMTb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)

Te6edf3hTb4b4b4.Te6edf3broadcasterTb4b4b4.Te6edf3startTb4b4b4(Te6edf3backgroundScopeTb4b4b4)
Te6edf3hTb4b4b4.Te6edf3settleTb4b4b4(Tb4b4b4)

Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3hTb4b4b4.Te6edf3serverManagerTb4b4b4.Te6edf3broadcastsTb4b4b4.Te6edf3sizeTb4b4b4, Ta5d6ff"Ta5d6ffonly the one eligible node should publishTa5d6ff"Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3meshNodeTb4b4b4(Te6edf3num Tff7b72= Te6edf3ELIGIBLE_NODE_NUMTb4b4b4)Tb4b4b4.Te6edf3cotUidTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3hTb4b4b4.Te6edf3serverManagerTb4b4b4.Te6edf3broadcastsTb4b4b4.Te6edf3singleTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3uidTb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ff`an unchanged node is not rebroadcast`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3h Tff7b72= Te6edf3HarnessTb4b4b4(Tff7b72thisTb4b4b4)Tb4b4b4.Te6edf3withConnectedClientTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3enableMeshToCotTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3node Tff7b72= Te6edf3meshNodeTb4b4b4(Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3nodeRepositoryTb4b4b4.Te6edf3setNodesTb4b4b4(Te6edf3listOfTb4b4b4(Te6edf3nodeTb4b4b4)Tb4b4b4)

Te6edf3hTb4b4b4.Te6edf3broadcasterTb4b4b4.Te6edf3startTb4b4b4(Te6edf3backgroundScopeTb4b4b4)
Te6edf3hTb4b4b4.Te6edf3settleTb4b4b4(Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3nodeRepositoryTb4b4b4.Te6edf3setNodesTb4b4b4(Te6edf3listOfTb4b4b4(Te6edf3nodeTb4b4b4)Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3settleTb4b4b4(Tb4b4b4)

Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3hTb4b4b4.Te6edf3serverManagerTb4b4b4.Te6edf3broadcastsTb4b4b4.Te6edf3sizeTb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ff`a moved node is rebroadcast`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3h Tff7b72= Te6edf3HarnessTb4b4b4(Tff7b72thisTb4b4b4)Tb4b4b4.Te6edf3withConnectedClientTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3enableMeshToCotTb4b4b4(Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3nodeRepositoryTb4b4b4.Te6edf3setNodesTb4b4b4(Te6edf3listOfTb4b4b4(Te6edf3meshNodeTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4)

Te6edf3hTb4b4b4.Te6edf3broadcasterTb4b4b4.Te6edf3startTb4b4b4(Te6edf3backgroundScopeTb4b4b4)
Te6edf3hTb4b4b4.Te6edf3settleTb4b4b4(Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3nodeRepositoryTb4b4b4.Te6edf3setNodesTb4b4b4(Te6edf3listOfTb4b4b4(Te6edf3meshNodeTb4b4b4(Te6edf3latitudeI Tff7b72= T79c0ff3T79c0ff8T79c0ff7Te6edf3_749_000Tb4b4b4)Tb4b4b4)Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3settleTb4b4b4(Tb4b4b4)

Te6edf3assertEqualsTb4b4b4(T79c0ff2Tb4b4b4, Te6edf3hTb4b4b4.Te6edf3serverManagerTb4b4b4.Te6edf3broadcastsTb4b4b4.Te6edf3sizeTb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ff`a connecting client gets a full replay`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3h Tff7b72= Te6edf3HarnessTb4b4b4(Tff7b72thisTb4b4b4)Tb4b4b4.Te6edf3withConnectedClientTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3enableMeshToCotTb4b4b4(Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3nodeRepositoryTb4b4b4.Te6edf3setNodesTb4b4b4(Te6edf3listOfTb4b4b4(Te6edf3meshNodeTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4)

Te6edf3hTb4b4b4.Te6edf3broadcasterTb4b4b4.Te6edf3startTb4b4b4(Te6edf3backgroundScopeTb4b4b4)
Te6edf3hTb4b4b4.Te6edf3settleTb4b4b4(Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3hTb4b4b4.Te6edf3serverManagerTb4b4b4.Te6edf3broadcastsTb4b4b4.Te6edf3sizeTb4b4b4)

T8b949e// The new client has seen none of the earlier broadcasts, so dedup must not suppress them.
Te6edf3hTb4b4b4.Te6edf3serverManagerTb4b4b4.Te6edf3emitClientConnectedTb4b4b4(Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3settleTb4b4b4(Tb4b4b4)

Te6edf3assertEqualsTb4b4b4(T79c0ff2Tb4b4b4, Te6edf3hTb4b4b4.Te6edf3serverManagerTb4b4b4.Te6edf3broadcastsTb4b4b4.Te6edf3sizeTb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ff`stationary nodes are refreshed before their markers go stale`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3h Tff7b72= Te6edf3HarnessTb4b4b4(Tff7b72thisTb4b4b4)Tb4b4b4.Te6edf3withConnectedClientTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3enableMeshToCotTb4b4b4(Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3nodeRepositoryTb4b4b4.Te6edf3setNodesTb4b4b4(Te6edf3listOfTb4b4b4(Te6edf3meshNodeTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4)

Te6edf3hTb4b4b4.Te6edf3broadcasterTb4b4b4.Te6edf3startTb4b4b4(Te6edf3backgroundScopeTb4b4b4)
Te6edf3hTb4b4b4.Te6edf3settleTb4b4b4(Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(T79c0ff1Tb4b4b4, Te6edf3hTb4b4b4.Te6edf3serverManagerTb4b4b4.Te6edf3broadcastsTb4b4b4.Te6edf3sizeTb4b4b4)

T8b949e// Nothing about the node changes, but ATAK expires the marker at MESH_NODE_STALE_MINUTES.
Te6edf3advanceTimeByTb4b4b4(Te6edf3MESH_TO_COT_REFRESH_INTERVAL_MS Tff7b72+ Te6edf3SETTLE_MSTb4b4b4)
Te6edf3runCurrentTb4b4b4(Tb4b4b4)

Te6edf3assertTrueTb4b4b4(Te6edf3hTb4b4b4.Te6edf3serverManagerTb4b4b4.Te6edf3broadcastsTb4b4b4.Te6edf3size Tff7b72>Tff7b72= T79c0ff2Tb4b4b4, Ta5d6ff"Ta5d6ffstationary node must be re-sent to stay alive in ATAKTa5d6ff"Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3MESH_TO_COT_REFRESH_INTERVAL_MS Tff7b72< Te6edf3MESH_NODE_STALE_MINUTES Tff7b72* T79c0ff6T79c0ff0L Tff7b72* T79c0ff1T79c0ff0T79c0ff0T79c0ff0LTb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ff`disabling the preference stops publishing`Tb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3h Tff7b72= Te6edf3HarnessTb4b4b4(Tff7b72thisTb4b4b4)Tb4b4b4.Te6edf3withConnectedClientTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3enableMeshToCotTb4b4b4(Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3nodeRepositoryTb4b4b4.Te6edf3setNodesTb4b4b4(Te6edf3listOfTb4b4b4(Te6edf3meshNodeTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4)

Te6edf3hTb4b4b4.Te6edf3broadcasterTb4b4b4.Te6edf3startTb4b4b4(Te6edf3backgroundScopeTb4b4b4)
Te6edf3hTb4b4b4.Te6edf3settleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3afterEnable Tff7b72= Te6edf3hTb4b4b4.Te6edf3serverManagerTb4b4b4.Te6edf3broadcastsTb4b4b4.Te6edf3size

Te6edf3hTb4b4b4.Te6edf3takPrefsTb4b4b4.Te6edf3isMeshToCotEnabledTb4b4b4.Te6edf3value Tff7b72= Tff7b72false
Te6edf3hTb4b4b4.Te6edf3nodeRepositoryTb4b4b4.Te6edf3setNodesTb4b4b4(Te6edf3listOfTb4b4b4(Te6edf3meshNodeTb4b4b4(Te6edf3latitudeI Tff7b72= T79c0ff3T79c0ff8T79c0ff7Te6edf3_749_000Tb4b4b4)Tb4b4b4)Tb4b4b4)
Te6edf3hTb4b4b4.Te6edf3settleTb4b4b4(Tb4b4b4)

Te6edf3assertEqualsTb4b4b4(Te6edf3afterEnableTb4b4b4, Te6edf3hTb4b4b4.Te6edf3serverManagerTb4b4b4.Te6edf3broadcastsTb4b4b4.Te6edf3sizeTb4b4b4)
Tb4b4b4}

Tff7b72private Tff7b72companion Tff7b72object Tb4b4b4{
Tff7b72const Tff7b72val Te6edf3OUR_NODE_NUM Tff7b72= T79c0ff0Te6edf3x0badf00d
Tff7b72const Tff7b72val Te6edf3ELIGIBLE_NODE_NUM Tff7b72= T79c0ff0Te6edf3xa1b2c3d4Tb4b4b4.Te6edf3toIntTb4b4b4(Tb4b4b4)
Tff7b72const Tff7b72val Te6edf3SETTLE_MS Tff7b72= T79c0ff1Te6edf3_000L

Tff7b72fun Td2a8ffrecentLastHeardTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3ClockTb4b4b4.Te6edf3SystemTb4b4b4.Te6edf3nowTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3epochSecondsTb4b4b4.Te6edf3toIntTb4b4b4(Tb4b4b4)

Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffLongParameterListTa5d6ff"Tb4b4b4)
Tff7b72fun Td2a8ffmeshNodeTb4b4b4(
Te6edf3numTb4b4b4: Tffa657Int Tff7b72= Te6edf3ELIGIBLE_NODE_NUMTb4b4b4,
Te6edf3latitudeITb4b4b4: Tffa657Int Tff7b72= T79c0ff3T79c0ff7T79c0ff7Te6edf3_749_000Tb4b4b4,
Te6edf3longitudeITb4b4b4: Tffa657Int Tff7b72= Tff7b72-T79c0ff1Te6edf3_224_194_000Tb4b4b4,
Te6edf3lastHeardTb4b4b4: Tffa657Int Tff7b72= Te6edf3recentLastHeardTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3hwModelTb4b4b4: Te6edf3orgTb4b4b4.Te6edf3meshtasticTb4b4b4.Te6edf3protoTb4b4b4.Te6edf3HardwareModel Tff7b72= Te6edf3orgTb4b4b4.Te6edf3meshtasticTb4b4b4.Te6edf3protoTb4b4b4.Te6edf3HardwareModelTb4b4b4.Te6edf3TBEAMTb4b4b4,
Tb4b4b4) Tff7b72= Te6edf3NodeTb4b4b4(
Te6edf3num Tff7b72= Te6edf3numTb4b4b4,
Te6edf3user Tff7b72=
Te6edf3orgTb4b4b4.Te6edf3meshtasticTb4b4b4.Te6edf3protoTb4b4b4.Te6edf3UserTb4b4b4(
Te6edf3id Tff7b72= Ta5d6ff"Ta5d6ff!Ta5d6ff" Tff7b72+ Te6edf3numTb4b4b4.Te6edf3toUIntTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3toStringTb4b4b4(T79c0ff1T79c0ff6Tb4b4b4)Tb4b4b4.Te6edf3padStartTb4b4b4(T79c0ff8Tb4b4b4, Ta5d6ff'0'Tb4b4b4)Tb4b4b4,
Te6edf3short_name Tff7b72= Ta5d6ff"Ta5d6ffWOLFTa5d6ff"Tb4b4b4,
Te6edf3long_name Tff7b72= Ta5d6ff"Ta5d6ffWolf Ridge RelayTa5d6ff"Tb4b4b4,
Te6edf3hw_model Tff7b72= Te6edf3hwModelTb4b4b4,
Tb4b4b4)Tb4b4b4,
Te6edf3position Tff7b72= Te6edf3orgTb4b4b4.Te6edf3meshtasticTb4b4b4.Te6edf3protoTb4b4b4.Te6edf3PositionTb4b4b4(Te6edf3latitude_i Tff7b72= Te6edf3latitudeITb4b4b4, Te6edf3longitude_i Tff7b72= Te6edf3longitudeITb4b4b4)Tb4b4b4,
Te6edf3lastHeard Tff7b72= Te6edf3lastHeardTb4b4b4,
Tb4b4b4)

Tff7b72fun Td2a8ffmyNodeInfoTb4b4b4(Te6edf3numTb4b4b4: Tffa657IntTb4b4b4) Tff7b72= Te6edf3MyNodeInfoTb4b4b4(
Te6edf3myNodeNum Tff7b72= Te6edf3numTb4b4b4,
Te6edf3hasGPS Tff7b72= Tff7b72falseTb4b4b4,
Te6edf3model Tff7b72= Tff7b72nullTb4b4b4,
Te6edf3firmwareVersion Tff7b72= Ta5d6ff"Ta5d6ff2.8.0Ta5d6ff"Tb4b4b4,
Te6edf3couldUpdate Tff7b72= Tff7b72falseTb4b4b4,
Te6edf3shouldUpdate Tff7b72= Tff7b72falseTb4b4b4,
Te6edf3currentPacketId Tff7b72= T79c0ff1LTb4b4b4,
Te6edf3messageTimeoutMsec Tff7b72= T79c0ff5T79c0ff0T79c0ff0T79c0ff0Tb4b4b4,
Te6edf3minAppVersion Tff7b72= T79c0ff1Tb4b4b4,
Te6edf3maxChannels Tff7b72= T79c0ff8Tb4b4b4,
Te6edf3hasWifi Tff7b72= Tff7b72falseTb4b4b4,
Te6edf3channelUtilization Tff7b72= T79c0ff0fTb4b4b4,
Te6edf3airUtilTx Tff7b72= T79c0ff0fTb4b4b4,
Te6edf3deviceId Tff7b72= Tff7b72nullTb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}

Served by rngit 1.5.0 - Generated in 0.07s